using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;

namespace ASPItalia.ModelVirtualCasting.WPF.DragDrop
{
    /// <summary>
    /// Class that act as drag'n'drop source element manager for ItemsControls
    /// </summary>
    public class ItemsControlDragSourceAdvisor : IDragSourceAdvisor
    {
        private UIElement _sourceUI;
        private bool _removeOnDrop = true;
        private Type _sourceItemType = typeof(ListBoxItem);

        /// <summary>
        /// Get the effects (copy, move, ...) supported by this source
        /// </summary>
        public DragDropEffects SupportedEffects
        {
            get { return DragDropEffects.Copy | DragDropEffects.Move; }
        }

        /// <summary>
        /// Get the effects (copy, move, ...) supported by this source
        /// </summary>
        public UIElement SourceUI
        {
            get
            {
                return _sourceUI;
            }
            set
            {
                _sourceUI = value;
            }
        }

        /// <summary>
        /// Get or set if after drop operations the advisor must remove the items from source. Default true
        /// </summary>
        public bool RemoveOnDrop
        {
            get { return _removeOnDrop; }
            set { _removeOnDrop = value; }
        }

        /// <summary>
        /// Get or set the ItemsControl container type to use for this drag'n'drop source. Default is ListBoxItem
        /// </summary>
        public Type SourceItemType
        {
            get { return _sourceItemType; }
            set
            {
                if (!value.IsSubclassOf(typeof(ContentControl)))
                    throw new ArgumentException("Type must be a ContentControl");
                _sourceItemType = value;
            }
        }

        /// <summary>
        /// Get the clipboard data to transfer from source to target
        /// </summary>
        /// <param name="draggedElt">Start dragging element</param>
        /// <param name="offsetPoint">Start dragging mouse position</param>
        /// <returns></returns>
        public DataObject GetDataObject(UIElement draggedElt, Point offsetPoint)
        {
            DataObject data = new DataObject();
            ContentControl uiItem = GetContainer(draggedElt);
            if (uiItem == null) return null;
            object item = GetItemsControlItem(uiItem);
            data.SetData(DataFormats.Locale, uiItem);
            data.SetData(DataFormats.Serializable, item);
            data.SetData("point", offsetPoint);

            return data;
        }

        /// <summary>
        /// Called when drag'n'drop operation is finished
        /// </summary>
        /// <param name="draggedElt"></param>
        /// <param name="finalEffects"></param>
        public void FinishDrag(UIElement draggedElt, DragDropEffects finalEffects)
        {
            if ((finalEffects & DragDropEffects.Move) == DragDropEffects.Move && this.RemoveOnDrop)
            {
                ItemsControl itemsControl = ItemsControl.ItemsControlFromItemContainer(this.SourceUI);
                object item = GetItemsControlItem(this.SourceUI);
                if (item == null) return;

                // Remove the item using source or items collection
                if (itemsControl.ItemsSource != null)
                {
                    IList list = itemsControl.ItemsSource as IList;
                    if (list == null)
                        throw new InvalidOperationException("Cannot remove item from ItemsSource");

                    list.Remove(item);
                }
                else
                {
                    itemsControl.Items.Remove(item);
                }
            }
        }

        /// <summary>
        /// Get if the start dragging element is valid for drag'n'drop operation
        /// </summary>
        /// <param name="draggedElt"></param>
        /// <returns></returns>
        public bool IsDraggable(UIElement draggedElt)
        {
            DependencyObject uiItem = GetContainer(draggedElt);
            return (uiItem != null);
        }

        private ContentControl GetContainer(UIElement dragElt)
        {
            DependencyObject parent = dragElt;
            while ((parent = VisualTreeHelper.GetParent(parent)) != null && (parent.GetType() != SourceItemType && !parent.GetType().IsSubclassOf(SourceItemType)))
            { }

            return parent as ContentControl;
        }

        private static object GetItemsControlItem(DependencyObject uiItem)
        {
            ItemsControl itemsControl = ItemsControl.ItemsControlFromItemContainer(uiItem);
            if (uiItem == null) return null;
            return itemsControl.ItemContainerGenerator.ItemFromContainer(uiItem);
        }
    }
}
